#pragma once
// 信号处理函数，本系统可能需要处理的信号都要放在这里
#include <signal.h>
#include <errno.h>
#include "log.h"
#include "global.h"

// 信号有关的结构体
typedef struct {
    int signo;// 信号对应的数字编号
    const char *signame;// 信号对应的字符串名 ，如SIGHUP
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);// 保存自定义的信号处理函数指针,参数和返回值是固定的(操作系统要求)
}signal_t;

// 信号处理函数的函数声明
static void Signal_handler(int signo, siginfo_t *siginfo, void *ucontext);

// 处理的信号数组，定义本系统处理的各种信号，在实际中，能想到要处理的都要包含
signal_t signals[] = {
    { SIGHUP,    "SIGHUP",           Signal_handler },// 终端断开信号，对于守护进程常用于reload重载配置文件通知--标识1
    { SIGINT,    "SIGINT",           Signal_handler },// 标识2,如用户按下Ctrl+C请求中断进程
    { SIGTERM,   "SIGTERM",          Signal_handler },// 标识15，系统或管理员发送的终止信号，如kill命令
    { SIGQUIT,   "SIGQUIT",          Signal_handler },// 标识3，用户按下Ctrl+\，请求进程退出并生产核心转储（记录进程内存状态和寄存器信息）
    { SIGIO,     "SIGIO",            Signal_handler },// 指示一个异步I/O事件【通用异步I/O信号】
    { SIGSYS,    "SIGSYS, SIG_IGN",  NULL               },// 表示收到了一个无效系统调用,NULL表示忽略掉
    // 日后需要补充
    { 0,         NULL,               NULL               }// 信号对应的数字至少是1，所以可以用0作为一个特殊标记
};

/**
 * 初始化信号的函数，用于注册信号处理程序
 * @return 返回0或-1，表示成功与否
 */
int Init_signals(){
    signal_t *sig;
    struct sigaction sa;// 系统定义的跟信号有关的一个结构

    for (sig = signals; sig->signo != 0; sig++){
        memset(&sa,0,sizeof(struct sigaction));
        // 如果信号处理函数不为空，表示要使用定义自己的信号处理函数
        if (sig->handler){
            sa.sa_sigaction = sig->handler;// 指定信号处理程序(函数)，注意sa_sigaction也是函数指针
            sa.sa_flags = SA_SIGINFO;// 指定信号的一些选项,SA_SIGINFO表示信号附带的参数可以被传递到信号处理函数中
        }
        else{
            sa.sa_handler = SIG_IGN;// 表示忽略信号的处理程序，否则操作系统的缺省信号处理程序很可能把这个进程杀掉
        }

        sigemptyset(&sa.sa_mask);// 把信号集中的所有信号清0，本意就是不准备阻塞任何信号

        // 设置信号处理动作(信号处理函数)
        // 参数1：要操作的信号
        // 参数2：主要就是那个信号处理函数以及执行信号处理函数时候要屏蔽的信号等等内容
        // 参数3：返回以往的对信号的处理方式，跟参数2同一个类型，这里不需要，所以直接设置为NULL
        if (sigaction(sig->signo, &sa, NULL) == -1){
            Log_Info(errno,"信号初始化函数对(%s)信号注册失败！ ",sig->signame);
            return -1;
        }
    }
    return 0;
}

/**
 * 自定义信号处理函数
 * @param signo 信号编号
 * @param siginfo 操作系统定义的结构体
 * @param ucontext 指向 ucontext_t 结构体的指针，提供信号触发时的完整线程上下文信息
 */
static void Signal_handler(int signo, siginfo_t *siginfo, void *ucontext){
    signal_t *sig;
    std::string action;// 记录一个动作字符串以便写入日志
    bool find = false;

    // 1.检查是否是需要处理的信号
    for (sig = signals; sig->signo != 0; sig++){
        if (sig->signo == signo){
            find = true;
            break;
        }
    }
    if(not find) return;// 找得到才处理

    // 2.信号处理
    switch (signo){
        case SIGHUP:// 终端断开信号
        {
            action = "Recv_Signal: SIGHUP(终端断开)\n";
            break;// 不处理，允许master进程关闭中断后台运行
        }
        case SIGINT:// 用户按下Ctrl+C请求中断进程
        case SIGTERM:// 系统或管理员发送的终止信号，如kill命令
        case SIGQUIT:// 用户按下Ctrl+\，请求进程退出并生产核心转储
        {
            // 根据进程类型打印日志
            action = std::string(g_title) + " Recv_Signal: SIGINT/SIGTERM/SIGQUIT(进程终止信号，开始退出.......!)\n";
            g_stopEvent = 1;// 更改系统标志，让事件处理循环自行检测并退出善后
            break;
        }
        case SIGIO:{// 指示一个异步I/O事件【通用异步I/O信号】
            action = "Recv_Signal: SIGIO(通用异步I/O信号)\n";
            break;
        }
        default:break;// 默认不处理
    }

    // 3.写入日志(使用移动语义避免内存被释放，引发写入错误)
    safe_write(g_log, action.c_str(), action.size());
}